package com.ruoyi.system.privilege;

import com.alibaba.fastjson.JSONArray;
import com.ruoyi.common.core.redis.RedisCache;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;

/**
 * 业务数据权限插件
 *
 * @author dbq
 * @date 2021/9/9 13:15
 */
@Intercepts({@Signature(type = StatementHandler.class,
        method = "prepare",
        args = {Connection.class, Integer.class})})
@Component
@Slf4j
public class PermissionHelper implements Interceptor {

    private static final String COUNT_PRE = "_COUNT";

    final RedisCache redisCache;

    final RuleParser ruleParser;

    public PermissionHelper(RedisCache redisCache, RuleParser ruleParser) {
        this.redisCache = redisCache;
        this.ruleParser = ruleParser;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        MetaObject statementHandler = SystemMetaObject.forObject(handler);
        MappedStatement ms = (MappedStatement) statementHandler.getValue("delegate.mappedStatement");
        BoundSql boundSql = handler.getBoundSql();

        //解析权限规则，并重新组装sql
        DataPermission anno = getPermissionByDelegate(ms);
        if (anno != null) {
            String point = anno.point();
            String sql = boundSql.getSql();
            log.info("SQL before interception：{}", sql);
            JSONArray permissions = JSONArray.parseArray(redisCache.getCacheObject("data_privilege_rule:"));
            String enhanceWhere = RuleParser.enhanceWhere(permissions, sql, point);
            log.info("SQL after interception：{}", enhanceWhere);
            statementHandler.setValue("delegate.boundSql.sql", enhanceWhere);
            return invocation.proceed();
        }

        return invocation.proceed();
    }


    /**
     * 获取数据权限注解信息
     */
    private DataPermission getPermissionByDelegate(MappedStatement mappedStatement) {
        DataPermission dataPermission = null;
        try {
            String id = mappedStatement.getId();
            if (id.contains(COUNT_PRE)) {
                id = id.replace(COUNT_PRE, "");
            }
            String className = id.substring(0, id.lastIndexOf("."));
            String methodName = id.substring(id.lastIndexOf(".") + 1);
            final Class<?> cls = Class.forName(className);
            final Method[] method = cls.getMethods();
            for (Method me : method) {
                if (me.getName().equals(methodName) && me.isAnnotationPresent(DataPermission.class)) {
                    dataPermission = me.getAnnotation(DataPermission.class);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataPermission;
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }

    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(properties);
    }
}

